package com.iisigroup.log.statistics;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;




import com.iisigroup.log.model.LogRecordV1;
import com.iisigroup.log.model.TaskSpendTime;

public class FileProcess {
    private static Logger logger = LoggerFactory.getLogger(FileProcess.class);
    private final   String pattern="(1[0-2]|0[1-9])\\/(3[01]|[12][0-9]|0?[1-9])\\-(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9]).\\d{3}";
    private final  String patternDateTime5="^(1[0-2]|0[1-9])\\/(3[01]|[12][0-9]|0?[1-9])\\-(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9]).*\\d?\\)$";
    private final  String patternDateTime6="^(1[0-2]|0[1-9])\\/(3[01]|[12][0-9]|0?[1-9])\\-(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9]).*";
    private final  String patternDateTime7=".*\\d?\\)$";
    
    public Map<String,List<LogRecordV1>> reagLogsByThread(final File... srcFiles){
	final Map<String,List<LogRecordV1>>  result  =new HashMap<String, List<LogRecordV1>>();
	
	final List<LogRecordV1> preparation = readLogs(srcFiles);
	for(LogRecordV1 unit : preparation){
	    final String threadName = unit.getThreadName();
	    List<LogRecordV1> unitList = result.get(threadName);
	    if(unitList == null ){
		unitList = new ArrayList<LogRecordV1>();
	    }
	    unitList.add(unit);
	    result.put(threadName, unitList);
	}
	return result;
    }

    public ConcurrentMap<String, List<LogRecordV1>> reagLogsByAccount(final File... srcFiles) {
	final ConcurrentMap<String, List<LogRecordV1>> result = new ConcurrentHashMap<String, List<LogRecordV1>>();

	final List<LogRecordV1> preparation = readLogs(srcFiles);
	for (LogRecordV1 unit : preparation) {
	    final String accountName = unit.getAccount();
	    List<LogRecordV1> unitList = result.get(accountName);
	    if (unitList == null) {
		unitList = new ArrayList<LogRecordV1>();
	    }
	    unitList.add(unit);
	    result.put(accountName, unitList);
	}
	return result;
    }
    public Map<String, List<LogRecordV1>> reagLogsByTaskName(final File... srcFiles) {
	final Map<String, List<LogRecordV1>> result = new HashMap<String, List<LogRecordV1>>();

	final List<LogRecordV1> preparation = readLogs(srcFiles);
	long logicProcessStartTime = System.nanoTime();
	for (LogRecordV1 unit : preparation) {
	    final String taskName = unit.getTaskName();
	    List<LogRecordV1> unitList = result.get(taskName);
	    if (unitList == null) {
		unitList = new ArrayList<LogRecordV1>();
	    }
	    unitList.add(unit);
	    result.put(taskName, unitList);
	}
	logger.debug("Time (seconds) taken  by reagLogsByTaskName is  {}", String.valueOf((System.nanoTime() - logicProcessStartTime) / 1.0e9));
	return result;
    }
    public List<LogRecordV1> readLogs(final File... srcFiles){
	long logicProcessStartTime = System.nanoTime();
	List<LogRecordV1> result = new ArrayList<LogRecordV1>();
	if(ArrayUtils.isNotEmpty(srcFiles)){
	    for(File file : srcFiles  ){
		logger.debug("file:  {}",file.getName());
		final	List<String> lineList = readLines(file);
		processConvert(result, lineList);
	    }
	}
	logger.debug("Time (seconds) taken  by reagLogs is  {}", String.valueOf((System.nanoTime() - logicProcessStartTime) / 1.0e9));
	return result;
    }
    public List<TaskSpendTime> readLogsForJoyce(final File... srcFiles){
	long logicProcessStartTime = System.nanoTime();
	final List<TaskSpendTime> result = new ArrayList<TaskSpendTime>();
	if(ArrayUtils.isNotEmpty(srcFiles)){
	    for(File file : srcFiles  ){
		logger.info("file:  {}",file.getName());
		final	List<String> lineList = readLines(file);
		final	List<TaskSpendTime> unitList = processConvert( lineList);
		result.addAll(unitList);
	    }
	}
	logger.info("Time (seconds) taken  by readLogsForJoyce is  {}", String.valueOf((System.nanoTime() - logicProcessStartTime) / 1.0e9));
	return result;
    }
    public List<TaskSpendTime> readLogsForJoyceV2(final File... srcFiles){
	long logicProcessStartTime = System.nanoTime();
//	final  ExecutorService executors = Executors.newCachedThreadPool();
	final int numberOfCores = Runtime.getRuntime().availableProcessors();
	logger.info("numberOfCores: {}",numberOfCores);
	final double blockingCoefficient = 0.99;
	final int poolSize = (int)(numberOfCores / (1 - blockingCoefficient));
	logger.info("poolSize: {}",poolSize);
	final ExecutorService executors = Executors.newFixedThreadPool(poolSize);
	
	final List<Future<List<TaskSpendTime>>> fsList =new ArrayList<Future<List<TaskSpendTime>>>();
	final List<Callable<List<TaskSpendTime>>> partitions =new ArrayList<Callable<List<TaskSpendTime>>>();
	
	
	
	final List<TaskSpendTime> result = new ArrayList<TaskSpendTime>();
	if(ArrayUtils.isNotEmpty(srcFiles)){
	    for(File file : srcFiles  ){
		logger.info("file:  {}",file.getName());
		final	List<String> lineList = readLines(file);		
		partitions.add(new Callable<List<TaskSpendTime>>() {		    
		    public List<TaskSpendTime> call() throws Exception {
			final	List<TaskSpendTime> unitList = processConvert( lineList);
			return unitList;
		    }
		});
	    }
	    try {
		executors.invokeAll(partitions,10000,TimeUnit.SECONDS);
		for (Future<List<TaskSpendTime>> fs : fsList) {
		    result.addAll(fs.get());
		}
	    } catch (InterruptedException e) {
		e.printStackTrace();
	    } catch (ExecutionException e) {
		e.printStackTrace();
	    }finally{
		executors.shutdown();
	    }
	}
	logger.info("Time (seconds) taken  by readLogsForJoyceV2 is  {}", String.valueOf((System.nanoTime() - logicProcessStartTime) / 1.0e9));
	return result;
    }
    protected List<TaskSpendTime> processConvert(final List<String> lineList) {
	long logicProcessStartTime = System.nanoTime();
	final List<TaskSpendTime> result = new ArrayList<TaskSpendTime>();
	final List<String> twoUnitList = new ArrayList<String>();
	for (int i = 0; i < lineList.size(); ++i) {
	    final String unit = lineList.get(i);

	    if (unit.matches(patternDateTime5)) {
		twoUnitList.add(unit);
	    } else if (unit.matches(patternDateTime6)) {
		twoUnitList.add(unit);
	    } else if (twoUnitList.size() > 0) {
		int index = twoUnitList.size() - 1;
		StringBuffer sbf = new StringBuffer();
		String newData = sbf.append(twoUnitList.get(index)).append("\n").append(unit).toString();
		twoUnitList.remove(index);
		twoUnitList.add(index, newData);
	    }
	}

	for (String unit : twoUnitList) {
	    if (StringUtils.contains(unit, "Profiler [AOP ")) {
		final LogRecordV1 unitResult = convert(unit);
		final TaskSpendTime taskTimeUnit = convertFromLogRec(unitResult);
		result.add(taskTimeUnit);
	    }

	}
	logger.info("Time (seconds) taken  by processConvert is  {}", String.valueOf((System.nanoTime() - logicProcessStartTime) / 1.0e9));
	return result;
    }
    protected void processConvert(final List<LogRecordV1> result, final List<String> lineList) {
	final List<String> twoUnitList = new ArrayList<String>();
	for (int i = 0; i < lineList.size(); ++i) {
	    final String unit = lineList.get(i);

	    if (unit.matches(patternDateTime5)) {
		twoUnitList.add(unit);
	    } else if (unit.matches(patternDateTime6)) {
		twoUnitList.add(unit);
	    } else if (twoUnitList.size() > 0) {
		int index = twoUnitList.size() - 1;
		StringBuffer sbf = new StringBuffer();
		String newData = sbf.append(twoUnitList.get(index)).append("\n").append(unit).toString();
		twoUnitList.remove(index);
		twoUnitList.add(index, newData);
	    }
	}

	for (String unit : twoUnitList) {
	    final LogRecordV1 unitResult = convert(unit);
	    result.add(unitResult);
	}
    }

   
    public TaskSpendTime convertFromLogRec(final LogRecordV1 src){
	final TaskSpendTime result = new TaskSpendTime();
	final	Field[] declaredField = src.getClass().getDeclaredFields();
	
	for(final Field aField : declaredField){
	    try {
		final String name = aField.getName();
		if("serialVersionUID".equals(name)){
		    continue;
		}
		Object   value =   PropertyUtils.getProperty(src, name);
		BeanUtils.copyProperty(result, name, value);
	    } catch (IllegalAccessException e) {
		e.printStackTrace();
	    } catch (InvocationTargetException e) {
		e.printStackTrace();
	    } catch (NoSuchMethodException e) {
		e.printStackTrace();
	    }
	}
	final	String msg = result.getMessage();
	final 	String[] lines = StringUtils.splitPreserveAllTokens(msg,"\n");
	final 	String patternMethodRegExr="\\s*\\-*\\+\\s*Profiler\\s{1}\\[AOP\\s{1}(.*?)\\].*";
	final	Pattern patternMethod = Pattern.compile(patternMethodRegExr);
	final 	String patternSpendTimeExr="-*\\s*Total\\s*.*\\[AuthorityFilter\\]\\s*(.*?)milliseconds.";
	final	Pattern patternSpendTime = Pattern.compile(patternSpendTimeExr);
	for(String line :lines){
	    final Matcher matcherMethod = patternMethod.matcher(line);
	    final Matcher matcherSpendTime = patternSpendTime.matcher(line);
	    if (matcherMethod.find()) {
		int groupCount = matcherMethod.groupCount();
		if(groupCount >=1 ){
		    String methodName =  matcherMethod.group(1);
		    result.setMethodName(methodName);
		}
	    }
	    if (matcherSpendTime.find()) {
		int groupCount = matcherSpendTime.groupCount();
		if(groupCount >=1 ){
		    String spendTime =  matcherSpendTime.group(1);
		    float spendMilliseconds = NumberUtils.toFloat(StringUtils.trim(spendTime));
		    result.setSpendMilliseconds(spendMilliseconds);
		}
	    }
	}
	
	
	
	
	
	return result; 
    }
    protected LogRecordV1 convert(final String unitLine){
	final SimpleDateFormat sdf = new SimpleDateFormat("MM/dd-HH:mm:ss.SSS");
	final LogRecordV1 result = new LogRecordV1();
	final String[] strArray = StringUtils.splitPreserveAllTokens(unitLine, "|");
	try {
	    final Date time = sdf.parse(strArray[0]);
	    result.setTime(DateUtils.setYears(time, 2014));
	} catch (ParseException e) {
	    e.printStackTrace();
	}
	result.setThreadName(strArray[1]);
	result.setCategory(strArray[2]);
	result.setAccount(StringUtils.trim(strArray[3]));
	result.setTaskName(strArray[4]);
	result.setTxId(StringUtils.trim(strArray[5]));
	result.setIp(StringUtils.trim(strArray[6]));
	result.setLogLevle(strArray[7]);
	
	if(strArray.length==9){
	    result.setMessage(strArray[8]);
	}else{
	    final StringBuffer sbf =new StringBuffer();
		    
	    for(int i = 8 ; i<strArray.length ;++i){
//		if(i<strArray.length){
//		    sbf.append(strArray[i]).append("\n");
//		}else{
//		    sbf.append(strArray[i]);
//		}
		 sbf.append(strArray[i]);
	    }
	    result.setMessage(sbf.toString());
	}
	result.setSrc(unitLine);
	return result ;
    }
    
    
    protected List<String> readLines(final File srcFile){
	List<String> result=null;
	BufferedReader br =null;
	try {
	    if(srcFile.exists() && srcFile.getName().contains(".gz")){
		result=new ArrayList<String>();
		final GZIPInputStream gzip = new GZIPInputStream(new FileInputStream(srcFile));
		br = new BufferedReader(new InputStreamReader(gzip));
		while(br.ready()){
		    result.add(br.readLine());
		}
		
	    }else{
		 result = FileUtils.readLines(srcFile);
	    }
	   
	} catch (IOException e) {
	    e.printStackTrace();
	} finally {
	    if (br != null) {
		try {
		    br.close();
		} catch (IOException e) {
		    e.printStackTrace();
		}
	    }
	}
	return result;
    }

}
